package com.zmx.common.common.config;

import org.apache.commons.lang3.concurrent.BasicThreadFactory;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;

import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * @Description: PoolHttpClient帮助类
 * @ClassName: HttpClientHelper
 * @Author zhaomxr
 * @Date 2021-11-30 15:45
 */
public class PoolHttpClientHelper {

    static PoolingHttpClientConnectionManager clientPool = new PoolingHttpClientConnectionManager();

    /**
     * httpclient请求超时配置
     */
    static RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(2000).setConnectTimeout(2000).build();

    /**
     * 将需要配置的值都放到静态代码块中，类被加载时，静态代码块也会被加载
     */
    static {
        // 最大连接请求数
        clientPool.setMaxTotal(100);
        // 最大路由连接数
        clientPool.setDefaultMaxPerRoute(100);
    }

    /**
     * 通过getHttpclientPool方法来获取httpclient，避免了直接去手动new
     * @return
     */
    public static CloseableHttpClient getHttpclientPool() {
        return HttpClients.custom().setConnectionManager(clientPool).setDefaultRequestConfig(requestConfig).build();
    }

    public static void closeExpiredConnections() {
        // 服务端假设关闭了连接，对客户端是不透明的，HttpClient为了缓解这一问题，在某个连接使用前会检测这个连接是否过时，如果过时则连接失效，但是这种做法会为每个请求
        // 增加一定额外开销，因此有一个定时任务专门回收长时间不活动而被判定为失效的连接，可以某种程度上解决这个问题
        ScheduledExecutorService executorService = new ScheduledThreadPoolExecutor(1,
                new BasicThreadFactory.Builder().namingPattern("closeExpiredConnections-schedule-pool-%d").daemon(true).build());
        executorService.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                try {
                    // 关闭失效连接并从连接池中移除
                    clientPool.closeExpiredConnections();
                    // 关闭30秒钟内不活动的连接并从连接池中移除，空闲时间从交还给连接管理器时开始
                    clientPool.closeIdleConnections(30, TimeUnit.SECONDS);
                } catch (Throwable t) {
                    t.printStackTrace();
                }
            }
        }, 0, 5, TimeUnit.SECONDS);
    }

}
